/**
*  \file sdfile.c
*
*  \brief	This module handles read/write access to/from the storage device
*
*  \author  Michael Kunkelmann  <m.kunkelmann@webergmbh.de>
*  \author Michael J. Hohmann	<michael.hohmann@linde-mh.de>
*
*/
/*===============================================[ private includes  ]================================================*/
#define SDFILE_LINKAGE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../sdfile/sdfile.h"
/*===============================================[ private defines   ]================================================*/

/*===============================================[ private datatypes ]================================================*/

/*===============================================[ private variables ]================================================*/

/*===============================================[ public variables  ]================================================*/

/*===============================================[ private functions ]================================================*/
static RC sdfile_read(struct sdfile_obj *me, void* dest, sd_address_t src, sd_size_t size );
static RC sdfile_write(struct sdfile_obj *me, sd_address_t dest, const void* src, sd_size_t size  );
static RC sdfile_copy(struct sdfile_obj *me, sd_address_t a, sd_address_t b, sd_size_t size  );
static RC sdfile_compare(struct sdfile_obj *me, void* a, sd_address_t b, sd_size_t *psize  );
static RC sdfile_set( struct sdfile_obj *me, sd_address_t dest, U8 value, sd_size_t size );

STATIC_INLINE BO check_access( struct sdfile_obj *me, sd_address_t addr, sd_size_t size )
{
	sd_address_t end_address = addr + size; /* on this will not be written */

	if( 0 != size )
	{
		end_address-=1;
	}

	if(end_address < addr) /* check for overflow */
	{
		return FALSE;
	}

	return (end_address) < (sd_address_t)(me->file_size);
}

/*====================================================================================================================*/

RC sdfile_construct( struct sdfile_obj *me, CH * txt,U32 sdfile_size,U8 init_value, BO force_cleanup )
{
	if( NULL == me )
	{
		return RC_ERROR_NULL;
	}

	if( 0 == sdfile_size )
	{
		return RC_ERROR_ZERO;
	}

	{
		RC state = RC_SUCCESS;

		me->object = fopen(txt,"rb+");	/* open file with attribute 'binary read' and 'binary write' */

		if(me->object == NULL)			/* check for existing file */
		{
			me->object = fopen(txt,"wb+");	/* a new file with sdfile_size */
			if(me->object != NULL)
			{
				force_cleanup = TRUE; /* because file is new, a initial setup is required */
			}
			else
			{
				state = RC_ERROR;
			}
		}

		me->dev.read   = (void*) sdfile_read;
		me->dev.write  = (void*) sdfile_write;
		me->dev.copy   = (void*) sdfile_copy;
		me->dev.set    = (void*) sdfile_set;
		me->dev.compare= (void*) sdfile_compare;
		me->file_size  = sdfile_size;

		if( force_cleanup )
		{
			sdfile_set( me, 0x00 , init_value, sdfile_size );
		}
		return state;
	}
}

RC sdfile_close( struct sdfile_obj *me )
{
	fclose(me->object);
	return RC_SUCCESS;
}

/** Function reads from storage device
*
*  This function reads size bytes from storage device copies it to dest.
*
* \param me pointer to object from struct sdfile_obj.
* \param dest destination where the data will be copied
* \param src data source
* \param size number of bytes which will be read
*
* \return RC at the moment always RC_SUCCESS
**/
static RC sdfile_read(struct sdfile_obj *me, void* dest, sd_address_t src, sd_size_t size )
{
	if( NULL == me )
	{
		return RC_ERROR_NULL;
	}

	if( NULL == dest )
	{
		return RC_ERROR_NULL;
	}

	if( check_access( me, src, size ) )
	{
		fseek(me->object, (S32)src, SEEK_SET);
		fread(dest,size,1,me->object);
		return RC_SUCCESS;
	}
	else
	{
		return RC_ERROR;
	}
}

/** Function writes to storage device
*
*  This function writes a fifo object from src to the dest in the storage device file
*
* \param me pointer to object from struct sdfile_obj, it's not save to pass NULL
* \param dest destination where the data will be copied
* \param src data source
* \param size number of bytes which will be read
*
* \return RC at the moment always RC_SUCCESS
*/
static RC sdfile_write(struct sdfile_obj *me, sd_address_t dest, const void* src, sd_size_t size  )
{
	if( NULL == me )
	{
		return RC_ERROR_NULL;
	}

	if( NULL == src )
	{
		return RC_ERROR_NULL;
	}

	if( check_access( me, dest, size ) )
	{
		fseek(me->object, (S32)dest, SEEK_SET);
		fwrite(src,1,size,me->object);
		fflush(me->object);
		return RC_SUCCESS;
	}
	else
	{
		return RC_ERROR;
	}
}

/** Copy function type.*/
static RC sdfile_copy(struct sdfile_obj *me, sd_address_t dest, sd_address_t src, sd_size_t size  )
{
	if( NULL == me )
	{
		return RC_ERROR_NULL;
	}

	if( check_access( me, dest, size ) && check_access( me, src, size ) )
	{
		while(size--)
		{
			U8 data;
			fseek(me->object, (S32)src, SEEK_SET);
			fread(&data, 1, 1, me->object);
			fseek(me->object, (S32)dest, SEEK_SET);
			fwrite(&data,1,1,me->object);
			dest++;
			src++;
		}

		return RC_SUCCESS;
	}
	else
	{
		return RC_ERROR;
	}
}

static RC sdfile_set( struct sdfile_obj *me, sd_address_t dest, U8 value, sd_size_t size )
{
	if( NULL == me )
	{
		return RC_ERROR_NULL;
	}

	if( check_access( me, dest, size ) )
	{
		fseek(me->object, (S32)dest, SEEK_SET);

		while(size--)
		{
			fwrite(&value,1,1,me->object);
		}

		return RC_SUCCESS;
	}
	else
	{
		return RC_ERROR;
	}
}


static RC sdfile_compare(struct sdfile_obj *me, void* a, sd_address_t b, sd_size_t *psize  )
{
	if( NULL == me )
	{
		return RC_ERROR_NULL;
	}

	if( NULL == a )
	{
		return RC_ERROR_NULL;
	}

	if( NULL == psize )
	{
		return RC_ERROR_NULL;
	}

	if( check_access( me, b, *psize ) )
	{
		U8 *p=a;
		sd_size_t size=*psize;
		sd_size_t count;

		fseek(me->object, (S32)b, SEEK_SET);
		for( count=0; count < size; count++)
		{
			U8 data;
			fread(&data, 1, 1, me->object);

			if(*p!=data)
			{
				*psize=count;
				if(*p<data)
				{
					return RC_ERROR_UNDERRUN;
				}
				else
				{
					return RC_ERROR_OVERRUN;
				}
			}
			p++;
		}
		*psize=count;
		return RC_SUCCESS;
	}
	else
	{
		*psize=0;
		return RC_ERROR;
	}
}

/***********************************************************************************************************************
*                             Copyright 2010 Linde Material Handling. All rights reserved.                             *
***********************************************************************************************************************/
